﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using iNet.Tasks;

namespace iNet.Media.Imaging.Filters
{
	/// <summary>
	/// 白平衡濾鏡工作。
	/// </summary>
	public class WhiteBalanceFilterTask : BitmapImageFilterTask
	{
		#region 欄位
		Color? _ReferenceWhiteColor;
		#endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 WhiteBalanceFilterTask 執行個體。
		/// </summary>
		public WhiteBalanceFilterTask()
		{ }
		/// <summary>
		/// 初始化新的 WhiteBalanceFilterTask 執行個體。
		/// </summary>
		/// <param name="sourceImage">來源點陣圖。</param>
		/// <param name="refWhiteColor">白色參考色，濾鏡將根據此顏色以調整影像的整體顏色，Null (Visual Basic 為 Nothing) 表示由濾鏡自行決定。</param>
		public WhiteBalanceFilterTask(IBitmapImage sourceImage, Color? refWhiteColor)
			: base(sourceImage)
		{
			_ReferenceWhiteColor = refWhiteColor;
		}
		#endregion
		#region ApplyFilter
		/// <summary>
		/// 套用濾鏡至點陣圖。
		/// </summary>
		/// <param name="version">工作版本。</param>
		/// <param name="filterData">套用濾鏡所需的資料。</param>
		/// <returns>True 表示濾鏡套用成功或取消，False 表示失敗。</returns>
		protected unsafe override bool ApplyFilter(TaskVersion version, BitmapImageFilterData filterData)
		{
			//取得資料
			Color? refWhiteColor;
			if (!this.ReadValue<Color?>(version, ref _ReferenceWhiteColor, out refWhiteColor))
				return true;

			//確認是否需轉換
			if (refWhiteColor.HasValue
				&& refWhiteColor.Value.R == refWhiteColor.Value.G
				&& refWhiteColor.Value.G == refWhiteColor.Value.B)
			{
				return true;
			}

			//計算色彩轉換
			bool is16BitsColor = filterData.Is16BitsColor;
			int colorCount = (is16BitsColor ? 65536 : 256);
			int maxColor = (colorCount - 1);
			double meanColor = (colorCount / 2);
			double transformRatioR;
			double transformRatioG;
			double transformRatioB;
			double maxTransformRatio = 3;
			double minTransformRatio = (1 / maxTransformRatio);
			double progress = 0;
			int width = filterData.PixelWidth;
			int height = filterData.PixelHeight;
			if (refWhiteColor == null)
			{
				//計算色彩分佈
				double accumPixelCount = 0;
				double accumR = 0;
				double accumG = 0;
				double accumB = 0;
				double maxDiff = (maxColor - meanColor);
				int whitePointThreshold = (int)((maxDiff * maxDiff + maxDiff * maxDiff) * 0.1);
				this.EnumerateSourceBitmapArgb(filterData, (x, y, a, r, g, b) =>
				{
					try
					{
						//中斷確認
						if ((y % 100) == 99 && this.IsCanceling)
							return false;

						//確認是否接近白色
						int m = ((r + g + b) / 3);
						if (m < meanColor)
							return true;
						int diffR = (r - m);
						int diffG = (g - m);
						int diffB = (b - m);
						int diff = (diffR * diffR + diffG * diffG + diffB * diffB);
						if (diff > whitePointThreshold)
							return true;

						//統計色彩
						double weight = ((double)m / maxColor);
						accumR += r * weight;
						accumG += g * weight;
						accumB += b * weight;
						accumPixelCount += weight;
						return true;
					}
					finally
					{
						if ((y % 100) == 99)
						{
							progress = ((y + 1) * 50.0 / height);
							this.ReportProgress(version, progress);
						}
					}
				});

				//中斷確認
				if (this.IsCanceling || accumPixelCount == 0)
					return true;

				//統計平均色
				int pixelCount = (filterData.PixelWidth * filterData.PixelHeight);
				double meanR = (accumR / accumPixelCount);
				double meanG = (accumG / accumPixelCount);
				double meanB = (accumB / accumPixelCount);
				meanColor = ((meanR + meanG + meanB) / 3);
				transformRatioR = (meanR > 0 ? (meanColor / meanR) : 1);
				transformRatioG = (meanG > 0 ? (meanColor / meanG) : 1);
				transformRatioB = (meanB > 0 ? (meanColor / meanB) : 1);
			}
			else
			{
				int refR, refG, refB;
				if (!is16BitsColor)
				{
					refR = refWhiteColor.Value.R;
					refG = refWhiteColor.Value.G;
					refB = refWhiteColor.Value.B;
				}
				else
				{
					refR = (refWhiteColor.Value.R * 256);
					refG = (refWhiteColor.Value.G * 256);
					refB = (refWhiteColor.Value.B * 256);
				}
				meanColor = ((refR + refG + refB) / 3);
				transformRatioR = (refR > 0 ? (meanColor / refR) : 1);
				transformRatioG = (refG > 0 ? (meanColor / refG) : 1);
				transformRatioB = (refB > 0 ? (meanColor / refB) : 1);
			}

			//修正轉換倍數
			if (transformRatioR > maxTransformRatio)
				transformRatioR = maxTransformRatio;
			else if (transformRatioR < minTransformRatio)
				transformRatioR = minTransformRatio;
			if (transformRatioG > maxTransformRatio)
				transformRatioG = maxTransformRatio;
			else if (transformRatioG < minTransformRatio)
				transformRatioG = minTransformRatio;
			if (transformRatioB > maxTransformRatio)
				transformRatioB = maxTransformRatio;
			else if (transformRatioB < minTransformRatio)
				transformRatioB = minTransformRatio;

			//建立轉換表
			int[] transformTableR = new int[colorCount];
			int[] transformTableG = new int[colorCount];
			int[] transformTableB = new int[colorCount];
			for (int i = maxColor; i >= 0; --i)
			{
				transformTableR[i] = Math.Min(maxColor, (int)(i * transformRatioR));
				transformTableG[i] = Math.Min(maxColor, (int)(i * transformRatioG));
				transformTableB[i] = Math.Min(maxColor, (int)(i * transformRatioB));
			}

			//調整色彩
			double baseProgress = progress;
			double progressRange = (100 - baseProgress);
			int stride = filterData.Stride;
			int bytesPerPixel = filterData.BytesPerPixel;
			byte* srcPixelLine = (byte*)filterData.SourcePixelBuffer;
			byte* resultPixelLine = (byte*)filterData.ResultPixelBuffer;
			for (int y = 0; y < height; ++y, srcPixelLine += stride, resultPixelLine += stride)
			{
				//調整色彩
				byte* srcPixel = srcPixelLine;
				byte* resultPixel = resultPixelLine;
				for (int x = width; x > 0; --x, srcPixel += bytesPerPixel, resultPixel += bytesPerPixel)
				{
					//取得色彩
					int r, g, b;
					if (!is16BitsColor)
					{
						r = srcPixel[2];
						g = srcPixel[1];
						b = srcPixel[0];
					}
					else
					{
						r = *(ushort*)(srcPixel);
						g = *(ushort*)(srcPixel + 2);
						b = *(ushort*)(srcPixel + 4);
					}

					//調整色彩
					r = transformTableR[r];
					g = transformTableG[g];
					b = transformTableB[b];

					//儲存新色彩
					if (!is16BitsColor)
					{
						resultPixel[2] = (byte)r;
						resultPixel[1] = (byte)g;
						resultPixel[0] = (byte)b;
					}
					else
					{
						*(ushort*)(resultPixel) = (ushort)r;
						*(ushort*)(resultPixel + 2) = (ushort)g;
						*(ushort*)(resultPixel + 4) = (ushort)b;
					}
				}

				//回報進度
				if ((y % 100) == 99)
				{
					//中斷確認
					if (this.IsCanceling)
						return true;

					//回報進度
					this.ReportProgress(version, baseProgress + ((y + 1) * progressRange / height));
				}
			}

			//完成
			return true;
		}
		#endregion
		#region ReferenceWhiteColor
		/// <summary>
		/// 取得或設定白色參考色，濾鏡將根據此顏色以調整影像的整體顏色，Null (Visual Basic 為 Nothing) 表示由濾鏡自行決定。
		/// </summary>
		public Color? ReferenceWhiteColor
		{
			get
			{
				return _ReferenceWhiteColor;
			}
			set
			{
				this.SetInitializingParameter<Color?>(ref _ReferenceWhiteColor, value);
			}
		}
		#endregion
	}
}
